Technical Note TN2042
Tailoring Java Applications for Mac OS X

目次

このテクニカルノートは、Mac OS X 上の Java アプリケーションにおいて「完全な Macintosh 体験」を提供するために Mac OS X の Java 開発者が利用できる技術と方法について説明します。 また、既存のアプリケーションをほかのプラットフォームからできるだけスムーズに Mac OS X に移植しようとしている Java 開発者も対象としています。

以下では、Java アプリケーションがより良き「Mac OS X 市民」になるよう、Java 環境特有の外観と動作を変更するために開発者ができることを取り上げます。 プロパティファイルに簡単な変更を加えれば済む場合もあれば、より適切な Java API を選択したり、UI に対してプログラム上で明示的に変更を加えなければならない場合もあります。これらの変更のほとんどは、ほかのプラットフォームでもアプリケーションの動作に悪い影響を及ぼさずに行えるので、Java が提供する移植性を維持できます。

本書で説明する内容は、Mac OS X 10.1.3 で Java 1.3.1 アップデート以降、および現在の開発ツールを使っている場合に適用されます。

[2002 年 5 月 23 日]






Macintosh クライアントの検出

本テクニカルノートのいくつかの推奨事項の中では、条件に応じて、API 呼び出しまたは UI コンポーネントの配置を行うことを推奨しています。アプリケーションを実行している OS を知る最も一般的で直観的な方法は、System プロパティである os.name を調べることです。しかし、この方法では、String ベースの結果を正しく解析する必要があるため、アップルがこのプロパティの設定方法を変更した場合は特に、エラーが生じやすくなります。Java クライアントが Macintosh かどうかを知るより信頼性の高い、より簡単な方法は、mrj.version プロパティを調べることです。

System.getProperty("mrj.version");

このプロパティは、どの Mac OS X システムでも Java 仮想マシンによって自動的に設定されます。テスト方法は簡単です。返される String がヌルでなければ、Macintosh 上で動作していることになります。これは、Macintosh 上で動作していることを確認する必要がある場合に推奨される方法です。

また、以降で取り上げる推奨事項の多くは、アップルの Aqua インタフェースに関するものです。つまり、Swing の Java(Metal)または Motif のルックアンドフィールには、有効または無効にしたくないかもしれないものも含まれています。Aqua が現在アクティブなルックアンドフィールであることを確認する一番良い方法は、

UIManager.getSystemLookAndFeelClassName()

を呼び出して、その結果を次の結果と比較することです。

UIManager.getLookAndFeel().getClass().getName()

これを、mrj.version のチェックと組み合わせることで、現在 Aqua を使っているかどうかを抽象的かつ適切な方法で調べることができます(最初に Macintosh 上で動作しているかどうかを調べ、次にシステムのルックアンドフィールが現在のルックアンドフィールと同じかどうかを調べます)。この方法なら、アップルが Aqua のルックアンドフィールに使っているクラスの名前を知っている必要がないので、アップルがルックアンドフィールのクラスの名前またはパッケージを変更したとしても、ロジックが必ず正しく動作することが保証されます。

先頭に戻る

Cocoa-Java の使用

Java アプリケーションを可能な限り Mac 風にする最も速くて最も明白な方法は、Cocoa-Java を使ってアプリケーションを作成することです。これにより、コンポーネントの推奨の配置やサイズにいたるまで、Interface Builder を使って、WYSIWYG により UI を手早く設計できるようになります。 Cocoa-Java のコントロールは、ムービーの簡単な埋め込みや真のフローティングツールバーウィンドウなど、Swing や AWT が提供していない機能を提供します。

もちろん、Cocoa-Java を使えば、移植性が高いという Java アプリケーションの特性は失われてしまいます。 このテクニカルノート では、ピュア Java アプリケーションに重点を置いていますが、 Cocoa-Java は言及するに値します。なぜなら、Cocoa-Java をユーザインタフェースに使用する場合、アプリケーションにおいて Cocoa ベースのコンポーネントと Carbon ベースのコンポーネントを混在させないことが重要だからです。Java に関係するものとしては、次のものがあります。

  • Java イベントを含む、Swing / AWT のコンポーネント
  • QuickTime for Java のコンポーネント
  • JDirect 呼び出し

一緒に使用できない主な理由は、Carbon と Cocoa では異なる実行ループを使っており、同じアプリケーションで使用すると衝突が生じるからです。QuickTime for Java が含まれているのが残念なようにも思えますが、Macintosh アプリケーションが必要とする簡単な QuickTime 機能(各種メディアファイルを開く、再生するなど)のほとんどは com.apple.cocoa.application.NSMovie クラスにより利用できます。

以上をふまえた上で、本文書の残りの部分では、Swing ベースおよび AWT ベースのピュア Java アプリケーションについて説明します。

先頭に戻る



ピュア JAVA で互換性を保つためのヒント

このテクニカルノートの前半では、ピュア Java アプリケーションにおいて可能な簡単な変更または選択について説明します。これらの変更は、Mac 固有の API とプロパティに関係するというより、1つのコードベースで Mac OS X とほかのプラットフォームを同じようにサポートするために頭に入れておかなければならない事柄です。

先頭に戻る

Mac OS X、Swing、Aqua

Mac OS X の Aqua ユーザインタフェースは、Java 自身のユーザインタフェースやほかのプラットフォームのユーザインタフェースとは大きく異なります。 Swing に対応した Aqua のプラガブルなルックアンドフィールが提供されていることは明らかに、Swing アプリケーションをより Mac 風にする上での強力な手段です。また、利用するために何の努力も要りません。つまり、コードを明示的な変更をしなくても、Mac OS X は、Swing アプリケーションに対して、起動時にデフォルトで Aqua のルックアンドフィールを採用します。

ルックアンドフィールとして Aqua が提供されていることから導き出される 1 つめの簡単な指針は、「Java アプリケーションの UI には Swing を使う」です。 Swing にはそのままで、AWT にまさる多数の基本的なメリットがあります。 そして、Aqua を利用することによって、AWT が提供する唯一ともいえるメリットもなくなります。 また、軽量と重量のコンポーネントを混在させると、パフォーマンスの上で、また描画の際に、アプリケーションに望ましくない影響を及ぼす可能性があります。

一番良いのは、Swing を使ってアプリケーションを設計するだけでなく、Java コードの中で明示的にルックアンドフィールの設定をしないようにすることです。 そして、別のプラットフォーム用にアプリケーションの ルックアンドフィールを変更する必要がある場合は、swing.defaultlaf という Java プロパティをオーバーライドするのが よりエレガントでより簡単な方法です。Mac OS X で Swing アプリケーションのルックアンドフィールを設定する方法の詳細については、Technical Q&A 1059 を参照してください。

Aqua の外観は自動的に採用されますが、 完全な Macintosh 体験を提供するために、依然として開発者に委ねられる領域および作業があります。 このテクニカルノートにおける指針の多くは、アップル開発者のための Web サイトにある Aqua Human Interface Guidelines (PDF) に準拠しています。次に、このガイドラインで取り上げられているいくつかの細かいながらも重要な指針の例をいくつか示します。

  • スクロールバーを常に表示されている状態にする (以下を参照)
  • ポップアップメニューの項目選択と設計(JComboBox
  • メニュー項目をグレー表示にする(無効にする)
  • メニュー項目用予約済みキーボードショートカット

Macintosh におけるユーザインタフェースの設計に関する詳細についてはこの文書を参照するよう強くお勧めします。我々も参照しています。

先頭に戻る

JDesktopPanes とマルチドキュメントインタフェース

ほとんどの Macintosh アプリケーションは、多数の、独立したフローティングウィンドウで構成されます。これらのウィンドウは、同じアプリケーションのコンテキストにはありますが、スクリーン上でアプリケーションウィンドウの境界として機能する親ウィンドウには含まれません。これは、 Macintosh がほかのプラットフォームと異なっている点でもあり、また、複数のウィンドウを持つアプリケーションを設計するときに考慮すべき点でもあります。

このため、絶対に必要という場合を除き、アプリケーションのメインの UI に、制約のある、親ウィンドウを持つ MDI モデルを採用する javax.swing.JDesktopPane クラスの使用は推奨しません。 例外は、フォーカスの有無に関わらず、常にアプリケーション内のほかのすべてのウィンドウよりも前面に表示される、フローティングツールバーのような要素(Swing では、「内部ユーティリティウィンドウ」と呼ばれています)を必要とする Java アプリケーションです。現在 Java には、JDesktopPane を使う以外にこれを提供する方法はありません。JBuilder または LimeWire のような Java アプリケーションが利用しているのと同じような、単一の動的コンテナを持つ、よりプラットフォームに依存しない UI の設計を考えるのもよいかもしれません。

MDI ベースのアプリケーションをほかのプラットフォームから Macintosh に 移植するときに、 階層ペイン構造なしに、既存のユーザ体験を提供するのが不可能だとわかる場合があります。その場合は、UI をそのままにしておく方がよいかもしれません。決めるのは開発者です。

付言として、Cocoa-Java を使って、ツールバー形式とパレット形式のコントロールを実装できます。ただし、Cocoa-Java を使用すれば、移植性は失われてしまい、Swing または AWT のコンポーネントは使えなくなります。

一般的には、アプリケーションにおいて JDesktopPane を使うことが、(独立した複数のフローティングウィンドウを持つ Mac OS の典型的な)アプリケーションに、複数のウィンドウを制御できる単一のメニューバーを提供する唯一の方法であること、また、これが、MDI ベースの UI を選択する主な理由になりえることもわかっています。以下の節では、MDI に代わる方法、および Swing のマルチフレームに関する問題の解決方法について説明します。

先頭に戻る

Swing のメニューとメニュー項目

クロスプラットフォーム対応の Java UI の開発においてもう 1 つ厄介なのは、メニュー項目のショートカットと外観がプラットフォームによって異なることです。残念ながら Java プログラマの多くは、開発時点で対象となっているプラットフォームだけを念頭に置いてアプリケーションを記述し、コードの中で明示的に修飾子またはトリガを指定します。そのため、プラットフォームに対応した正しいトリガが誤って解釈される危険が生まれるだけでなく、アプリケーションをほかのプラットフォームに移植するのが難しくなります。

幸い、任意のプラットフォームで、これらのアクションを作成するより洗練された解決方法があります。しかもこの方法なら移植性があります。

メニューショートカット: メニューショートカットは多くの場合、開発者が java.awt.KeyStroke を使って明示的に指定することによって設定されます。この方法では、異なるショートカット修飾子を持つ別のプラットフォームに移植するときに、新しいクライアントプラットフォームで条件に応じて新たな KeyStroke を作成する必要があるので複雑になります。これを解決するには、次の AWT メソッドを使います。

java.awt.Toolkit.getMenuShortcutKeyMask()

このメソッドを呼び出すと、現在のプラットフォームの Toolkit の実装が適切なマスクを返します。この呼び出し 1 つで、現在のプラットフォームを調べ、その後、どれが正しいキーかを判断してくれます。リスト 1 にこれを示します。

 リスト 1 getMenuShortcutKeyMask() を使用したメニューショートカットの簡素化

JMenuItem jmi = new JMenuItem("Copy");

/*
    // TN2042 で紹介する方法を知る前に、自分が実行できたのはこれだけ
    String vers = System.getProperty("os.name").toLowerCase();
    if (s.indexOf("windows") != -1) {
       jmi.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_C, Event.CTRL_MASK));
    } else if (s.indexOf("mac") != -1) {
       jmi.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_C, Event.META_MASK));
    }
*/

// この 1 行は、C がコピーのショートカットであるすべてのプラットフォームで有効

jmi.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_C,
    Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()));
                            

Macintosh 上で行われる一般的なアクションのほとんどに、Command キーを唯一の修飾子としてではないにしろ少なくとも修飾子の 1 つとして使う、対応するキーボードショートカットがあります。残念ながら、ほかのプラットフォームにはこれほどの一貫性はなく、たとえば、Alt キーを使うキーボードショートカットもあるでしょう。上記の呼び出しは、一般的なマスクを 1 つ返すだけなので、ホストのプラットフォームを基準にした条件に基づいてショートカットキーを設定する必要があるかもしれません。Macintosh で対応するキーボードショートカットのほとんどは Command キーを使っているため、アプリケーションを Windows に移植しようとしていて、Windows 上でアクションを正しく再現できるかを心配している場合は、移植は少し楽でしょう。最も一般的な予約済みのショートカットの信頼できるリストについては、Aqua Human Interface Guidelines の keyboard equivalents の節を参照してください。

ALT_MASK 修飾子は、Macintosh 上では Option キーと解釈されるので、ALT_MASKgetMenuShortcutKeyMask() と組み合わせて使えば、Windows 用に設定した Ctrl + Alt のマスクはそのまま Cmd + Opt のマスクになります。

メニューアクセスキー: ほかのプラットフォームとは異なり、Mac OS X (Aqua)は、メニューアクセスキー、つまり メニュー(Alt キーを使用)とメニュー項目(親メニューを開いているときに表示される)のための単一キーによるショートカットを提供しません。 アクセスキーは通常、メニュー(またはメニュー項目)の名前の中で 1 文字にだけ下線が付いて表示されます。 これまで Macintosh アプリケーションでメニューアクセスキーが使用されたことはありませんが、可能ならばGUI を構築するときに、コードの中で条件に応じて setMnemonics() メソッドを呼び出すなど、プラットフォームに応じてアクセスキーを利用することを推奨します。

メニュー項目のアイコン: アクセスキーと同様、Swing によりメニュー項目でアイコンも利用できます(Mac OS X 上で機能します)が、Aqua's Human Interface Guidelines の標準には含まれていません。Macintosh の Java アプリケーションを、Carbon アプリケーションまたは Cocoa アプリケーションのような外観にするためには、これらのアイコンをプラットフォームに応じて適用するとよいでしょう。

アプリケーションがどのプラットフォーム上で動作しているかを調べる方法の詳細については、Macintosh クライアントの検出 の節を参照してください。

先頭に戻る

コンテキストメニュー

ほとんどのプラットフォームは、なんらかの形でコンテキストメニューをサポートしているので、Java アプリケーションにコンテキストメニューを含めることには何の問題もありません(Java ではこの種のメニューを PopupMenu と呼び、Aqua ではこの用語を、Java で JComboBox と呼んでいるものに対して使います)。しかし、コンテキストメニューが、どのプラットフォームでも同じ方法で同じタイミングで、明示的にトリガされると考えているとすれば、プラットフォーム間での移植は、実際には複雑かもしれません。たとえば、Windows においては、右のマウスボタンがコンテキストメニューの標準のトリガです。標準の Macintoshのマウス(1 つボタンのマウス)では、コンテキストメニューは Control + クリックで表示されます。


注意:
サードパーティ製の Macintosh 用マルチボタンマウスが問題になることはないでしょう。そのようなマウスの右ボタンは通常、ドライバにより Control + クリックに対応付けられているからです。


これら 2 つは大きく異なるので、上記のメニューのコード例のように、断片的で、条件分岐するコードになるでしょう。 ただし、マウスクリックだけは共通です。プログラムに、プラットフォームに関係なく、コンテキストメニューのトリガを正しく解釈させるためには、次のインスタンスメソッドを使って、AWT に解釈をさせます。

java.awt.event.MouseEvent.isPopupTrigger()

このメソッドは、MouseEvent の中で定義します。なぜなら任意のコンポーネントに対する MouseEvent が検出されたとき、そのコンポーネントを対象とする MouseListener でコンテキストメニューをアクティブにするからです。ここで大事なのは、正しいイベントを検出する方法とタイミングです。

 リスト 2  isPopupTrigger() を使用してコンテキストメニューの起動を検出

JLabel label = new JLabel("I have a popup menu!");

label.addMouseListener(new MouseAdapter(){
    public void mousePressed(MouseEvent e) {
       evaluatePopup(e);
    }

    public void mouseReleased(MouseEvent e) {
       evaluatePopup(e);
    }

    private void evaluatePopup(MouseEvent e) {
       if (e.isPopupTrigger()) {
          // ポップアップメニューを表示...
       }
    }
});
                            

上記のコード例は、MOUSE_PRESSED イベントと MOUSE_RELEASED イベントの両方で isPopupTrigger() をチェックしています。なぜなら、 Macintosh では、ポップアップトリガは、MOUSE_PRESSED のときに設定され、 Windows では、MOUSE_RELEASED のときに設定されるからです。移植性を維持するには両方の場合に対応しなければなりません。

先頭に戻る

コンポーネントの配置と描画

Swing の UI は、ネスト構造になっているコンテナとコンポーネントを組み合わせることによって構築され、多くの場合複雑になります。1 つのプラットフォームを対象に作業していると、開発者は、自分の UI の設計が、ほかのプラットフォーム上ではどのように見えるかという視点を簡単に見失ってしまいます。言葉自体が示すとおり、ルックアンドフィールが異なるということは、見た目(ルック)と使い心地(フィール)が違うということです。プラットフォームが異なり、ルックアンドフィールが異なれば、フォントサイズ、ボタンのサイズと形状、背景色と前景色などすべてが変わり得ます。コンポーネントの配置、サイズ決めおよび描画に、抽象化されたメソッドや一般化されたメソッドを使うことが極めて重要なのはこのためです。

レイアウトマネージャ: レイアウトマネージャを利用するのは、多くの開発者にとって、非常に簡単な作業かもしれませんが、多くのプログラマは、明示的にコントロールの X と Y の座標を設定することによって、アプリケーションを微調整しようとします。 このようにして作成されたアプリケーションが、ある時点で、別のルックアンドフィール、別のプラットフォームで動作する場合、コンポーネントが、互いに重なって描画されたり、コンテナの境界をはみ出したりなどひどい UI になる可能性があります。一般論として、座標を明示的に指定してボタンとコントロールを配置しても移植性があると仮定するのは危険です。ルックアンドフィールが異なれば、コントロールのサイズの違いから、UI は突然、意図したものとはものとは全く違って見える可能性があります。AWT のレイアウトマネージャは、抽象化された位置定数(GridLayout による相対的なグリッド座標、BorderLayout による配置など)を使うことによってこの問題を解決します。これらのコントロールの正確な配置は、レイアウトマネージャが、コンテナにおける各コンポーネントの相対的な配置を維持しながら、各コンポーネントのサイズを考慮して決定されます。

コンポーネントのサイズ: コンポーネントのサイズを明示的に設定するのも場合によっては、危険な習慣です。ルックアンドフィールごとに、フォントのスタイルとサイズが異なるかもしれないからです。これらのフォントサイズはほぼ確実に、テキストを含むコンポーネントの必要サイズに影響を与えるでしょう。明示的にサイズ指定されたコンポーネントを、より大きなフォントサイズを持つ別のルックアンドフィールに移すと大きな問題を引き起こす可能性があります。UI コンポーネントを、移植性を維持しながら、適正な最小サイズに保つ最も安全な方法は、ただ次のメソッドを呼び出すことです。

myComponent.setSize(myComponent.getPreferredSize());

ここで、myComponent は、対象とする UI コンポーネントオブジェクトです。ほとんどのレイアウトマネージャとコンテナは、コンポーネントの推奨サイズを尊重するので、多くの場合この呼び出しは不要です。しかし、UI がより複雑になるに従い、多数の子コンポーネントを持つコンテナにとって、この呼び出しは有用です。

コンポーネントの色: どのルックアンドフィールでも、すべてではないにしろ、ほとんどのコントロールで共通の色合いとスタイルを採用しているので、開発者は標準の UI クラスのルックアンドフィールに調和するカスタムのコンポーネントを作成しようという気になることもあるでしょう。これは完全に正当なことですが、不注意な開発者は、現在のルックアンドフィールに一致すると考える色を明示的に設定してしまうかもしれません。その場合、ルックアンドフィールを変えると、コンテナのどこかにあるボタンが非常に見苦しくなることがあります。カスタムのコントロールとその他の標準のコンポーネントとを確実に一致させる一番良い方法は、最適な色またはアイコンを UIManager クラスに問い合わせることです。一番良い例は、標準の軽量コンポーネントをいくつか含んでいるけれども、露出した背景を、アプリケーションのその他のコンテナやウィンドウの背景に合うように描画する必要がある Window オブジェクトでしょう。そのために、開発者は次のメソッドを呼び出します。

myPanel.setBackground(UIManager.getColor("window"))

これは、現在のルックアンドフィールにふさわしい色を返します。これらの標準メソッドを使うもう 1 つの利点は、これらの標準メソッドが、簡単には再現できない特殊な背景とアイコン(Aqua のコンテナとウィンドウに使うしま模様の背景など。これは、上記コードにより返されます)も提供していることです。

先頭に戻る

スクロールバー付きのウィンドウ(JScrollPanes の使用)

Aqua Human Interface Guidelines が推奨する UI 設計の具体例の 1 つに、ウィンドウ内容のナビゲーションにスクロールバーを使うウィンドウがあります。デフォルトでは、JFrame は、どのようにサイズ変更しても、スクロールバーが付きません。 フレームにおいて内容をスクロールできるようにする一番簡単な方法は、フレームのコンポーネントを JScrollPane の中に入れ、それを親フレームに追加することです。ただし、JScrollPane のデフォルトの動作では、縦方向と横方向のスクロールバーは必要な場合にのみ、つまりペインの内容全体が表示されていない場合にのみ表示されます。これは、いくつかのプラットフォームでも同じかもしれませんが、スクロール可能なウィンドウは、常にスクロールバーを表示するよう推奨している Aqua Human Interface Guidelines に反することになります。この規定は、スクロールバーが必要に応じて表示されたり消えたりすることによって、ウィンドウの表示可能な領域が小さくなったり大きくなったりするという錯覚を起こすような、紛らわしい UI をなくすためです。Java アプリケーションの中で JScrollPane を使う場合は、無効になっているときでも常にスクロールバーを表示するように JScrollPane のスクロールバーポリシーを設定することを推奨します。リスト 3 は、標準の Java API を使ってこれを行う方法を示します。

 リスト 3 Aqua Human Interface Guidelines に適合するように JScrollBar ポリシーを設定


JScrollPane jsp = new JScrollPane();
jsp.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
jsp.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);


AS_NEEDED (デフォルト)のポリシーは、ほかのプラットフォームでの動作により近いかもしれないので、ホストプラットフォームに応じてこれを行うようにすることはもちろん可能です。 このような変更は、UI の仕様が少ないほかのプラットフォームでは、まったく気付かれない場合もあります。最終的に決めるのは開発者です。常に表示させる場合、JScrollPane の内容全体が表示されているときは、スクロールバーは単色で表示され、スクローラーがないことに留意してください(身近な例として、Finder でウィンドウの動作を確かめてください)。

先頭に戻る

Aqua のファイルダイアログ

java.awt.FileDialog クラスと javax.swing.JFileChooser クラスは、Java アプリケーションのユーザのために、ファイルシステムへの手早く簡単なアクセス手段を提供する 2 つの主要な機能です。JFileChooser の方が、より新しく、抽象化された、よりカスタマイズしやすいクラスで、旧式の、ネイティブ API から派生した AWT の FileDialog の後継にあたります。これらのクラスはそれぞれ他方にはない利点を持っています。

一般的には、アプリケーションをできるだけネイティブの外観にするために、開発者は、AWT の FileDialog クラスを使ってユーザにファイルへのアクセス手段を提供することが推奨されています。これらの 2 つのクラスの違いは、Carbon アプリケーションと Cocoa アプリケーションのファイルダイアログでカラム形式のナビゲーションが使用される Mac OS X 上で Aqua を使用した場合には特に顕著です。このナビゲーション方式は、AWT の FileDialog により自動的に採用されるのに対し、Swing の JFileChooser は、典型的な Mac OS X アプリケーションのナビゲーション方式とは異なる方式を使います。しかし、JFileChooser が持つ多数の機能上の利点の方が、推奨の方法よりも有用かもしれません。決めるのは開発者です。ダイアログがモーダルであり、常に、コンポーネントよりも前面に描画されるので、Swing アプリケーションに重量の FileDialog を使っても、重大な影響が生じることはありません。

.pkg ファイルと .app ファイルの処理: Java アプリケーションの中で Mac OS X のアプリケーションバンドルとインストーラパッケージを扱う方法にも考慮が必要です。.app ファイルと .pkg ファイルは、厳密にいえばディレクトリであり、JFileChooser または FileDialog を使う Java アプリケーションは最初はそのように認識し、不正なナビゲーションを許可します。 アップルでは、これらのクラスの両方に、.pkg インストールパッケージと .app バンドル(両方とも実際はディレクトリ)の処理方法を制御するのに使えるプロパティを提供しています。

FileDialog: AWT の FileDialog クラスでは、次のシステムランタイムプロパティを使って、.pkg ファイルと .app ファイルをナビゲーション不可能なものとして処理するように設定できます。

com.apple.macos.use-file-dialog-packages

指定可能な値は、true(.pkg と .app をファイルとして処理)と false(フォルダとして処理、デフォルトの動作)。このプロパティは、テクニカルノート 2031 で説明されているように、システムのランタイムプロパティのための標準の機能を使って設定でき、設定後はすべての FileDialog インスタンスに適用されます。これにより開発者は、コードを変更することなく AWT ダイアログを Aqua に対応するように変更できます。アプリケーションにおいて、インスタンスごとに動作を変える必要がある場合には、実行時に、必要に応じて System.setProperty() を使って、このプロパティを true または false に設定できます。または、インスタンスごとにJFileChooser のクライアントプロパティを利用することもできます。

JFileChooser: JFileChooser の使用時、.app ファイルと .pkg ファイルの処理を指示する 2 つのプロパティがあります。どちらもプログラミングによりアプリケーションの中で設定する必要があります(コードに変更を加える必要があります)。.pkg インストーラパッケージと .app アプリケーションバンドルのプロパティはそれぞれ次のとおりです。

JFileChooser.packageIsTraversable

JFileChooser.appBundleIsTraversable

これらのプロパティに指定可能な値は、always (フォルダとして処理、デフォルトの動作)と never(ファイルとして処理)です。これらのプロパティをアプリケーションにおける JFileChooser の全インスタンスに対してグローバルに設定するには、UIManager.put() を使います。または、JComponent から継承した putClientProperty() インスタンスメソッドを使って、インスタンスごとに設定することもできます。

これらのプロパティの使用に関しては、いくつかの既知の問題があります。

  • 現時点では、 JFileChooser.packageIsTraversablenever に設定すると、.pkg ファイルと .app ファイル
  • どちらについてもそのように設定されます。
  • 現時点では、 JFileChooser.appBundleIsTraversablenever に設定すると、packageIsTraversable が無視され、JFileChooser で動作がデフォルトの動作となり、.pkg ファイルがナビゲーション可能になります。

つまり、現時点では、これらのプロパティを使用した場合、.app ファイルのみ、または .app ファイルと .pkg ファイルの両方をナビゲーション不可能にできます。


注意:
JFileChooser プロパティは、Swing で Aqua のルックアンドフィールを使う場合にだけ有効です。なぜなら、Aqua はアップルが制御する唯一のルックアンドフィールだからです。


先頭に戻る



Macintosh 固有の仕立て

次の数節では、Java アプリケーションを、可能な限り Aqua に対応した Mac OS X アプリケーションに近づけるという具体的な目的のために、Java アプリケーションに対して行える変更や設計上の決定について説明します。ほかのプラットフォームでは影響のない変更もあれば、アプリケーションを複数のプラットフォームで動作させる場合には、コードを条件に応じて、パッケージ化または実行する必要がある変更もあります。

先頭に戻る

Macintosh のメニューバーの使用

Java の UI モデルと Macintosh の UI モデルの違いの 1 つは、Swing ではアプリケーションのメニューバーはフレーム(ウィンドウ)ごとに適用されるということです。Windows のモデルと同じように、Swing によるアプリケーションのメニューバーは、フレームのタイトルバーの直下に表示されます。これは、アプリケーションのすべてのウィンドウを制御する「スクリーン」メニューバーが、アプリケーションに 1 つだけある Macintosh のモデルとは異なります。この問題を簡単に解決するために、次のランタイムプロパティが追加されています。

com.apple.macos.useScreenMenuBar

このプロパティは、 true または false の値を持つことができます。未定義の場合、false の値に対応する標準の Java の動作が適用されます。アプリケーションの起動時にJava のランタイムにより読み取られると、指定された JFrameJMenuBar が、Macintosh ユーザが想定する位置、つまりスクリーンの最上部に配置されます。これは、ホストの仮想マシンが使用しなければならない単純なランタイムプロパティなので、アプリケーションで設定しても、ほかのプラットフォームでは、そのチェックすら行われないので、何の影響もありません。

JDialog に関連付けられている JMenuBar は、このプロパティがセットされていても想定位置である最上部ではなく、(プロパティがセットされていないかのように)ダイアログの内部に表示されることを知っておくことは重要です。 デフォルトでは、メニューバーのない JDialog にフォーカスがあたると、親の JFrame のメニューバーは、選択不可で透けて見えます。

ダイアログウィンドウにメニューを付ける必要を感じた場合には、UI を再考した方がよいでしょう。ダイアログは、情報提供するか、ユーザに 1 つの簡単な決定を提示するものでなければなりません。メニューバーを必要とする機能を持つウィンドウは、JFrame にしておく方が良いかもしれません。

スクリーンメニューバーを有効にする方法の簡単な要約は、Q&A 1003 からも入手できます。

残念ながら、これにより解決されるのは問題の一部(視覚的な配置)にすぎません。「ウィンドウごとに 1 つのメニューバーを」という根本的な問題は依然存在します。つまり、メニューは、スクリーンの最上部に表示されますが、メニューが割り当てられている特定のウィンドウにフォーカスがあたっているときだけです。アプリケーションに複数のウィンドウがあり、メニューバーを持つウィンドウ以外のウィンドウにフォーカスがあたったときには、メニューバーが消えてしまいます。Aqua Human Interface Guidelines では、アプリケーションのメニューバーは常に表示されていなければならないと規定されています。アラートダイアログのような重要でないウィンドウでもメニューバーが表示されなければなりません(このダイアログのメニューは無効にした方がよいかもしれませんが)。

この問題に対処する方法はいくつかありますが、最も一般的な方法は、アプリケーションの各独立フレームに同じメニューバーを生成するメニューファクトリを作成し使用することです。こうすることで、フォーカスが各フレームに移っても、フレームには、スクリーンの最上部にメニューバーがそのまま表示されています。

先頭に戻る

ウィンドウメニュー

Aqua Human Interface Guidelines の指示の 1 つに、Mac OS X アプリケーションはすべて、現在開かれているすべてのウィンドウを常に把握できるようにするために、 ウィンドウメニューを提供するべきだとあります。Java の Swing でこの機能を実装するのは不可能ではありませんが、中でも、このテクニカルノートで示した 2 つの推奨事項、つまりMacintosh のメニューバーと独立したフローティングフレームに関する推奨事項を応用することが要求されます。 これら 2 つの要件を満たさなくても、ウィンドウメニューは実装できるものの、これらは、Java アプリケーションを可能な限り Mac 風にするために相乗的に機能します。メニューバーをすべてに表示するという問題と同様、Swing の現在のアーキテクチャでは、アプリケーションの現在の状態に合わせて、常に更新され同期化される複数のメニューとメニュー項目を持たせるのは困難です。 しかし、ActionPropertyChangeListener を利用すれば、ウィンドウメニューの機能のほとんどは、現在利用できる機能で実現できます。

ウィンドウメニューには、現在アクティブな(可視)ウィンドウのリストが、現在選択され最前面に表示されているウィンドウがあればそれに対応するメニュー項目にチェックマークが付いた状態で、表示されていなければなりません。また、特定のウィンドウのメニュー項目を選択することで、対応するウィンドウが最前面に来なければなりません。 新しく開かれたウィンドウはメニューに追加され、閉じられたウィンドウはメニューから取り除かれなければなりません。メニュー項目の順序は通常、ウィンドウが表示された順序です(詳細な説明については Aqua Human Interface Guidelines を参照)。

先頭に戻る

アプリケーションメニュー

AWT または Swing を使っている Java アプリケーション、およびダブルクリック起動が可能な .app ファイルにパッケージ化されている Java アプリケーションはすべて自動的に、Mac OS X のネイティブアプリケーションと同様、アプリケーションメニューを使って起動されます。このアプリケーションメニューには、デフォルトでは、タイトルとしてメインクラスの完全な名前が含まれています。この名前は、 com.apple.mrj.application.apple.menu.about.name というアプリケーションプロパティを使って、または -Xdock:name コマンドラインプロパティを使って変更できます。Mac OS X の Aqua Human Interface Guidelines によれば、アプリケーションメニューに指定する名前は 16 文字を超えてはいけません。詳細は、Java Runtime Properties for Mac OS X(Mac OS X の Java ランタイムプロパティ) に関するテクニカルノートおよび Aqua Human Interface Guidelines を参照してください。

アプリケーションメニューをカスタマイズするための次の手順は、アプリケーションメニューの項目が選択されたときに、独自に作成した処理コードが実際に呼び出されるようにすることです。アップルでは、このために、com.apple.mrj パッケージの特別な Java インタフェースを使った手段を提供しています。各インタフェースにはそれぞれ、適切なアプリケーションメニューが選択されたときに呼び出される特別なコールバックメソッドがあります。Java 1.3.1 Update 1 for Mac OS X 以降では、アプリケーションメニューのために次のコールバックインタフェースが利用できます。

  • MRJAboutHandler - プログラムが、[<アプリケーションの名前> について...]メニュー項目の選択に対応できるようにします。
  • MRJPrefsHandler - [環境設定...]メニュー項目に対応します。
  • MRJQuitHandler - [<アプリケーションの名前> を終了 ]メニュー項目が選択されたときの最後のクリーンアップ処理を行います。

Java Application Menu

図 1 Mac OS X 上の Java アプリケーションのアプリケーションメニュー

特定のアプリケーションメニュー項目を処理するには、次の手順で行います。

  1. 適切なハンドラインタフェースを実装します。
  2. 実装の中で適切なハンドラメソッド (handleAbout()handlePrefs()handleQuit())を定義します。
  3. com.apple.mrj.MRJApplicationUtils クラスの適切なスタティックメソッド(registerAboutHandler()registerPrefsHandler()registerQuitHandler())を使ってハンドラを登録します。

これらの実装例は、Project Builder で新しい Java Swing アプリケーションを開くだけで見ることができます。 Mac OS X 10.1 と 10.2 (Jaguar)における、MRJQuitHandler の正しい使い方を示す Technical Q&A も参考にしてください。

先頭に戻る

アプリケーションメニューのカスタマイズ

Mac OS X 上の Java アプリケーションへのアプリケーションメニューの追加について、またそれらのメニュー項目の利用方法についてはすでに説明しました 。ほかのプラットフォームにアプリケーションを配備する予定があり、メニューバー内のほかの場所([ファイル]メニュー、[編集]メニューなど)に、[環境設定]、[終了]、または[...について]の各項目を配置する必要がある場合は、条件に応じてこの配置を行なった方がよいでしょう。たとえば、2 つの異なる[環境設定]メニュー項目があったり、MRJ の[終了]項目と同時に[終了]メニュー項目があったりしても害はありませんが、Mac ユーザにとっては、アプリケーションメニューにおなじみの項目がアプリケーションメニューにだけあって、ほかのどこにもない方が紛らわしくないでしょう。これは小さな変更ですが、Mac OS X の Java アプリケーションのルックアンドフィールでは大きな違いを生むかもしれません。

先頭に戻る

追加の MRJ ハンドラ

アプリケーションメニューを扱うために提供されているインタフェースのほかに、現在の Mac OS X に対応した Java のリリースでは、2 つの機能的な MRJ ハンドラがサポートされています。

  • MRJOpenApplicationHandler - アプリケーションを開くというアップルイベントに対応
  • MRJOpenDocumentHandler - サポートされているドキュメントのダブルクリックまたはアプリケーションアイコンへのドラッグに対応

これらのハンドラインタフェースは、Mac OS X の Finder における Java アプリケーションの動作の強化を目的とするもので、上記のアプリケーションメニューインタフェースと同じように使用します。アプリケーションが Finder から MRJOpenDocumentHandler を使って開けるファイル形式の登録は、アプリケーションバンドルの Info.plist ファイルの最上位レベルで追加のキーを使って、Cocoa アプリケーションまたは Carbon アプリケーションの場合と同じように行います。詳細については、『Bundle Keys』の CFBundleDocumentTypes の節を参照してください。

先頭に戻る

Java アプリケーションの起動

Macintosh 体験の最も身近なそして顕著な特徴はおそらく、アプリケーションがダブルクリック起動が可能なアイコンまたはパッケージにより起動されることでしょう。Java アプリケーションが、「良き Mac OS X 市民」になるためには、起動のためにコマンドラインの使用を要求するべきではありません。エンドユーザのために、Java アプリケーションをダブルクリック起動可能にする方法がいくつかあります。

  • Project Builder: ダブルクリック起動が可能な Java アプリケーションを作成する最も簡単な方法は、最初から Project Builder でプロジェクトを開始することです。ビルド時に、作成した Java ライブラリの回りに .app パッケージが自動的に構築されます。Project Builder は、Mac OS X Developer Tools と一緒にインストールされます。
  • MRJAppBuilder: これは、ほかのツールを使って、ほかのプラットフォーム向けにすでに作成されたアプリケーションにとっては、おそらく最も魅力的な方法です。MRJAppBuilder は、必要なすべての JAR ファイルと引数に基づいて、Mac OS X 用に .app パッケージを構築します。 これは、Mac OS X Developer Tools と一緒にインストールされます。
  • Java Web Start: Mac OS X 10.1 から提供が開始された Java Web Start は、システムと一緒にインストールされます。Web Start は、Web リンクをクリックするだけで簡単に Web 上にアプリケーションを配置できる素晴らしい手段です。 現在の実装では、JNLP ファイルは、ユーザの downloads ディレクトリに保存され、これをダブルクリックすると、Web Start によってアプリケーションが起動されます。より新しいバージョンが元の URL で入手可能になれば、そのダウンロードまで行います。Web Start の詳細については、Sun の Web サイトを参照するか、Java Web Start(/Applications/Utilities/)を開いて、サンプルアプリケーションを実際に使って試してください。
  • マニフェストファイル: アプリケーションが完全に単独の JAR ファイルに含まれていて、メインクラスを定義する適切なマニフェストファイルがある場合は、Mac OS X の Finder の中で JAR そのものをダブルクリックするだけでアプリケーションを起動できます。

もちろん .app による方法が、Java アプリケーションをパッケージ化する最も Mac 風な方法です。

Dock アイコンの設定

Mac OS X で起動されるアプリケーションにはすべて、Finder の Dock の中に対応するアイコンがあります。これは、Mac OS X のグラフィカルな Java アプリケーションだけでなく、.app バンドルでパッケージ化されている非グラフィカルなアプリケーションの場合も同様です。デフォルトの Java アイコンが、Dock に入れるひな形として提供されていますが、開発者は、次の 2 つのどちらかの方法を使って Java アプリケーションのカスタムアイコンを指定することができます。

  1. Terminal: 起動引数、-XDock:icon を使います。
  2. Bundle (.app): バンドルの Contents/Resources/ ディレクトリに .icns ファイルを含めます。

このことは、ダブルクリック起動が可能な JAR ファイルを使って配備されるアプリケーションは、Dock アイコンをカスタマイズできないことを意味します。したがって、上に挙げたほかの配備方法を検討するようお勧めします。残念ながら、Java Web Start がアプリケーションを起動する方法に起因して、現時点では、Java Web Start でも Dock アイコンのカスタマイズは不可能です。

-XDock 引数の詳細については、Java Runtime Properties for Mac OS X(Mac OS X の Java ランタイムプロパティ) に関するテクニカルノートを参照してください。

先頭に戻る



要約

このテクニカルノートでは、開発者が、Mac OS X で動作する Java アプリケーションに、Mac OS X のユーザ体験の利点をもたらすことができる領域について簡単に見てきました。Mac OS X 上の Java アプリケーションを、できるだけネイティブアプリケーションらしい外観にするために、開発者の方々が、ランタイムプロパティから、互換性を保つためのベストプラクティス、Mac 固有の微調整に至るまで、利用できる手段を最大限に活用できるよう願っています。

先頭に戻る

ダウンロード

Acrobat gif

このテクニカルノートの PDF 版

ダウンロード


先頭に戻る